/*
 * pgp-pubenc.c
 *	  使用公钥加密会话密钥。
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * contrib/pgcrypto/pgp-pubenc.c
 */
#include "postgres.h"

#include "pgp.h"
#include "px.h"

/*
 * 填充消息: 02 || 非零填充字节 || 00 || 消息
 */
static int fc_pad_eme_pkcs1_v15(uint8 *fc_data, int fc_data_len, int fc_res_len, uint8 **fc_res_p)
{
	uint8	   *fc_buf,
			   *fc_p;
	int			fc_pad_len = fc_res_len - 2 - fc_data_len;

	if (fc_pad_len < 8)
		return PXE_BUG;

	fc_buf = palloc(fc_res_len);
	fc_buf[0] = 0x02;

	if (!pg_strong_random(fc_buf + 1, fc_pad_len))
	{
		pfree(fc_buf);
		return PXE_NO_RANDOM;
	}

	/* 填充不能包含零字节 */
	fc_p = fc_buf + 1;
	while (fc_p < fc_buf + 1 + fc_pad_len)
	{
		if (*fc_p == 0)
		{
			if (!pg_strong_random(fc_p, 1))
			{
				px_memset(fc_buf, 0, fc_res_len);
				pfree(fc_buf);
				return PXE_NO_RANDOM;
			}
		}
		if (*fc_p != 0)
			fc_p++;
	}

	fc_buf[fc_pad_len + 1] = 0;
	memcpy(fc_buf + fc_pad_len + 2, fc_data, fc_data_len);
	*fc_res_p = fc_buf;

	return 0;
}

static int fc_create_secmsg(PGP_Context *fc_ctx, PGP_MPI **fc_msg_p, int fc_full_bytes)
{
	uint8	   *fc_secmsg;
	int			fc_res,
				fc_i;
	unsigned	fc_cksum = 0;
	int			fc_klen = fc_ctx->sess_key_len;
	uint8	   *fc_padded = NULL;
	PGP_MPI    *fc_m = NULL;

	/* 计算校验和 */
	for (fc_i = 0; fc_i < fc_klen; fc_i++)
		fc_cksum += fc_ctx->sess_key[fc_i];

	/*
	 * 创建“秘密消息”
	 */
	fc_secmsg = palloc(fc_klen + 3);
	fc_secmsg[0] = fc_ctx->cipher_algo;
	memcpy(fc_secmsg + 1, fc_ctx->sess_key, fc_klen);
	fc_secmsg[fc_klen + 1] = (fc_cksum >> 8) & 0xFF;
	fc_secmsg[fc_klen + 2] = fc_cksum & 0xFF;

	/*
	 * 现在将其创建为大整数
	 */
	fc_res = fc_pad_eme_pkcs1_v15(fc_secmsg, fc_klen + 3, fc_full_bytes, &fc_padded);
	if (fc_res >= 0)
	{
		/* 第一个字节将是 0x02 */
		int			fc_full_bits = fc_full_bytes * 8 - 6;

		fc_res = pgp_mpi_create(fc_padded, fc_full_bits, &fc_m);
	}

	if (fc_padded)
	{
		px_memset(fc_padded, 0, fc_full_bytes);
		pfree(fc_padded);
	}
	px_memset(fc_secmsg, 0, fc_klen + 3);
	pfree(fc_secmsg);

	if (fc_res >= 0)
		*fc_msg_p = fc_m;

	return fc_res;
}

static int fc_encrypt_and_write_elgamal(PGP_Context *fc_ctx, PGP_PubKey *fc_pk, PushFilter *fc_pkt)
{
	int			fc_res;
	PGP_MPI    *fc_m = NULL,
			   *fc_c1 = NULL,
			   *fc_c2 = NULL;

	/* 创建填充消息 */
	fc_res = fc_create_secmsg(fc_ctx, &fc_m, fc_pk->pub.elg.p->bytes - 1);
	if (fc_res < 0)
		goto err;

	/* 加密它 */
	fc_res = pgp_elgamal_encrypt(fc_pk, fc_m, &fc_c1, &fc_c2);
	if (fc_res < 0)
		goto err;

	/* 写出 */
	fc_res = pgp_mpi_write(fc_pkt, fc_c1);
	if (fc_res < 0)
		goto err;
	fc_res = pgp_mpi_write(fc_pkt, fc_c2);

err:
	pgp_mpi_free(fc_m);
	pgp_mpi_free(fc_c1);
	pgp_mpi_free(fc_c2);
	return fc_res;
}

static int fc_encrypt_and_write_rsa(PGP_Context *fc_ctx, PGP_PubKey *fc_pk, PushFilter *fc_pkt)
{
	int			fc_res;
	PGP_MPI    *fc_m = NULL,
			   *fc_c = NULL;

	/* 创建填充消息 */
	fc_res = fc_create_secmsg(fc_ctx, &fc_m, fc_pk->pub.rsa.n->bytes - 1);
	if (fc_res < 0)
		goto err;

	/* 加密它 */
	fc_res = pgp_rsa_encrypt(fc_pk, fc_m, &fc_c);
	if (fc_res < 0)
		goto err;

	/* 写出 */
	fc_res = pgp_mpi_write(fc_pkt, fc_c);

err:
	pgp_mpi_free(fc_m);
	pgp_mpi_free(fc_c);
	return fc_res;
}

int pgp_write_pubenc_sesskey(PGP_Context *fc_ctx, PushFilter *fc_dst)
{
	int			fc_res;
	PGP_PubKey *fc_pk = fc_ctx->pub_key;
	uint8		fc_ver = 3;
	PushFilter *fc_pkt = NULL;
	uint8		fc_algo;

	if (fc_pk == NULL)
	{
		px_debug("no pubkey?\n");
		return PXE_BUG;
	}

	fc_algo = fc_pk->algo;

	/*
	 * 现在写入数据包
	 */
	fc_res = pgp_create_pkt_writer(fc_dst, PGP_PKT_PUBENCRYPTED_SESSKEY, &fc_pkt);
	if (fc_res < 0)
		goto err;
	fc_res = pushf_write(fc_pkt, &fc_ver, 1);
	if (fc_res < 0)
		goto err;
	fc_res = pushf_write(fc_pkt, fc_pk->key_id, 8);
	if (fc_res < 0)
		goto err;
	fc_res = pushf_write(fc_pkt, &fc_algo, 1);
	if (fc_res < 0)
		goto err;

	switch (fc_algo)
	{
		case PGP_PUB_ELG_ENCRYPT:
			fc_res = fc_encrypt_and_write_elgamal(fc_ctx, fc_pk, fc_pkt);
			break;
		case PGP_PUB_RSA_ENCRYPT:
		case PGP_PUB_RSA_ENCRYPT_SIGN:
			fc_res = fc_encrypt_and_write_rsa(fc_ctx, fc_pk, fc_pkt);
			break;
	}
	if (fc_res < 0)
		goto err;

	/*
	 * 完成，信号数据包结束
	 */
	fc_res = pushf_flush(fc_pkt);
err:
	if (fc_pkt)
		pushf_free(fc_pkt);

	return fc_res;
}
